#pragma once

#include <iostream>
#include <string>
#include <sys/types.h>
#include <sys/stat.h>
#include <fcntl.h>
#include <cstdio>
#include <unistd.h>

//报错的宏
#define ERR_EXIT(m) \
do \
{ \
    perror(m); \
    exit(EXIT_FAILURE); \
}while(0)


#define PATH "."
#define FILENAME "fifo"    //文件名  提供路径
// #define FIFO_FILE "fifo"

// 这就是实现了一个命名管道
class NamedFifo
{
public:
    // 构造   创建命名管道
    NamedFifo(const std::string &path, const std::string &name)
        : _path(path), _name(name)
    {
        _fifoname = _path + "/" + _name;

        umask(0);
        // 新建管道  创建管道文件
        int n = mkfifo(_fifoname.c_str(), 0666);
        if (n < 0)
        {
            // std::cerr << "mkfifo error" << std::endl;

            //失败直接退出
            // perror("mkfifo:");
            // exit(1);

            //或者错误退出
            ERR_EXIT("mkfifo");   //会进行宏替换  替换成为打印错误消息
        }
        else
        {
            std::cout << "mkfifo success" << std::endl;
        }
    }

    ~NamedFifo()
    {
        // 删除管道文件
        int n = unlink(_fifoname.c_str()); // 析构
        if (n == 0)
        {
            // std::cout << "remove fifo success" << std::endl;

            ERR_EXIT("unlink");
        }
        else
        {
            std::cout << "remove fifo failed" << std::endl;
        }
    }

private:
    std::string _path;
    std::string _name;
    std::string _fifoname;
};

// 这个就是实现一个 IO操作类  文件操作类
class FileOper
{
public:
    FileOper(const std::string &path, const std::string &name)
        : _path(path), _name(name), _fd(-1)
    {
        _fifoname = _path + "/" + _name;
    }

    void OpenForRead()
    {
     //当write 方没有执行open的时候，read方，就要在open内部进行阻塞，直到有人把管道文件打开，open才会返回
        // server 进行读取   read
        _fd = open(_fifoname.c_str(), O_RDONLY);
        if (_fd < 0)
        {
            // std::cerr << "open fifo error" << std::endl;
            // return;

            ERR_EXIT("open");
        }
        std::cout << "open fifo success" << std::endl; // 读取
    }

    void OpenForWrite()
    {
        // write  向文件进行写入
        // 要注意：当write方法没有执行 open 的时候 ，read读取方就需要在open内部进行阻塞，直到有人把管道文件打开了，open才会但会！！！
        _fd = open(_fifoname.c_str(), O_WRONLY);
        if (_fd < 0)
        {
            // std::cerr << "open fifo error" << std::endl;
            // return;

            ERR_EXIT("open");
        }
        std::cout << "open fifo success" << std::endl;
    }

    void Write()
    {
        // 实现写入操作
        std::string message;
        int cnt = 1;
        pid_t id = getpid(); // 当前子进程的pid信息
        while (true)
        {
            std::cout << "Please Enter# " ;
            std::getline(std::cin, message);    // 用getline 从标准输入cin中获得内容 获取的内容放到message中  获得了用户输入的字符串
            message += (",message number: " + std::to_string(cnt++) + "[" + std::to_string(id) + "]"); // 转化成为字符串
            write(_fd, message.c_str(), message.size());        // 发出去  也就是往文件中写，就可以写给对方了
        }
    }

    void Read()
    {
        // 正常的读取  read
        while (true)
        {
            char buffer[1024];
            int number = read(_fd, buffer, sizeof(buffer) - 1);
            if (number > 0)
            {
                buffer[number] = 0;
                std::cout << "Client Say# " << buffer << std::endl;
            }
            else if (number == 0)
            {
                // 当写端进程关闭，曾打开的管道文件也会被直接关掉（文件的生命周期是随着进程有效的   client进程关闭，读端就会读到返回值0）
                // 也就是不再是上面的 大于0         而是这里的等于0
                std::cout << "client quit! me too!" << std::endl;
                break;
            }
            else
            {
                std::cerr << "read error" << std::endl;
                break;
            }
        }
    }

    void Close()
    {
        if (_fd > 0)
            close(_fd);
    }

    ~FileOper()
    {

    }

private:
    std::string _path;
    std::string _name;
    std::string _fifoname;
    int _fd;
};
